
package com.zy.je.common.core.utils.collect;

import com.zy.je.common.core.constant.StringPool;
import com.zy.je.common.core.model.ITreeNode;
import com.zy.je.common.core.model.KeyValue;
import com.zy.je.common.core.utils.CheckUtil;
import com.zy.je.common.core.utils.lang.Fn;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.json.JSONUtil;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * List工具类
 *
 * @author zy
 */
@Slf4j
@SuppressWarnings("rawtypes")
public class ListUtil extends org.dromara.hutool.core.collection.ListUtil {


    public static <E> ArrayList<E> newArrayList() {
        return new ArrayList<E>();
    }

    @SafeVarargs
    public static <E> ArrayList<E> newArrayList(E... elements) {
        ArrayList<E> list = new ArrayList<E>(elements.length);
        Collections.addAll(list, elements);
        return list;
    }

    public static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
        return (elements instanceof Collection) ? new ArrayList<E>(cast(elements)) : newArrayList(elements.iterator());
    }

    public static <E> ArrayList<E> newArrayList(Iterator<? extends E> elements) {
        ArrayList<E> list = newArrayList();
        addAll(list, elements);
        return list;
    }

    public static <E> LinkedList<E> newLinkedList() {
        return new LinkedList<E>();
    }

    public static <E> LinkedList<E> newLinkedList(Iterable<? extends E> elements) {
        LinkedList<E> list = newLinkedList();
        addAll(list, elements);
        return list;
    }

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
        return new CopyOnWriteArrayList<E>();
    }

    public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(Iterable<? extends E> elements) {
        Collection<? extends E> elementsCollection = (elements instanceof Collection) ? cast(elements)
                : newArrayList(elements);
        return new CopyOnWriteArrayList<E>(elementsCollection);
    }

    private static <T> Collection<T> cast(Iterable<T> iterable) {
        return (Collection<T>) iterable;
    }

    private static <T> boolean addAll(Collection<T> addTo, Iterator<? extends T> iterator) {
        boolean wasModified = false;
        while (iterator.hasNext()) {
            wasModified |= addTo.add(iterator.next());
        }
        return wasModified;
    }

    public static <T> boolean addAll(Collection<T> addTo, Iterable<? extends T> elementsToAdd) {
        if (elementsToAdd instanceof Collection) {
            Collection<? extends T> c = cast(elementsToAdd);
            return addTo.addAll(c);
        }
        return addAll(addTo, elementsToAdd.iterator());
    }

    /**
     * 判断是否为空.
     */
    public static boolean isEmpty(Collection collection) {
        return (collection == null || collection.isEmpty());
    }

    /**
     * 判断是否为不为空.
     */
    public static boolean isNotEmpty(Collection collection) {
        return !(isEmpty(collection));
    }

    /**
     * 取得Collection的第一个元素，如果collection为空返回null.
     */
    public static <T> T getFirst(Collection<T> collection) {
        if (isEmpty(collection)) {
            return null;
        }
        return collection.iterator().next();
    }

    /**
     * 获取Collection的最后一个元素 ，如果collection为空返回null.
     */
    public static <T> T getLast(Collection<T> collection) {
        if (isEmpty(collection)) {
            return null;
        }
        // 当类型为List时，直接取得最后一个元素 。
        if (collection instanceof List) {
            List<T> list = (List<T>) collection;
            return list.get(list.size() - 1);
        }
        // 其他类型通过iterator滚动到最后一个元素.
        Iterator<T> iterator = collection.iterator();
        while (true) {
            T current = iterator.next();
            if (!iterator.hasNext()) {
                return current;
            }
        }
    }

    /**
     * 返回a+b的新List.
     */
    public static <T> List<T> union(final Collection<T> a, final Collection<T> b) {
        List<T> result = new ArrayList<T>(a);
        result.addAll(b);
        return result;
    }

    /**
     * 返回a-b的新List.
     */
    @Deprecated
    public static <T> Collection<T> subtractCollection(final Collection<T> a, final Collection<T> b) {
        Collection<T> list = new ArrayList<T>(a);
        for (T element : b) {
            list.remove(element);
        }
        return list;
    }

    /**
     * 返回a-b的新List.
     */
    @Deprecated
    public static <T> List<T> subtract(final Collection<T> a, final Collection<T> b) {
        List<T> list = new ArrayList<T>(a);
        for (T element : b) {
            list.remove(element);
        }
        return list;
    }

    /**
     * 返回a与b的交集的新List.
     */
    @Deprecated
    public static <T> List<T> intersection(Collection<T> a, Collection<T> b) {
        List<T> list = new ArrayList<T>();
        for (T element : a) {
            if (b.contains(element)) {
                list.add(element);
            }
        }
        return list;
    }

    /**
     * aList-bList的新List(在aList中，但不在bList中的数据)，做减法运算时的key由keyMapper提供
     *
     * @param aList
     * @param bList
     * @return
     */
    public static <K, T> List<T> subtract(Collection<T> aList, Collection<T> bList, Function<T, K> keyMapper) {
        Map<K, T> bMap = toMap(bList, keyMapper);
        return aList.stream().map(o -> {
            if (null == bMap.get(keyMapper.apply(o))) {
                return o;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 返回aList与bList的交集的新List.
     */
    public static <K, T> List<T> intersection(Collection<T> aList, Collection<T> bList, Function<T, K> keyMapper) {
        Map<K, T> bMap = toMap(bList, keyMapper);
        return aList.stream().map(o -> {
            if (null != bMap.get(keyMapper.apply(o))) {
                return o;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }


    /**
     * 取List指定位置的值，不存在时用指定的默认值代替
     *
     * @param list
     * @param index
     * @param defaultValue
     * @return
     */
    public static <T> T get(List<T> list, int index, T defaultValue) {
        if (isEmpty(list)) {
            return defaultValue;
        }
        if (index < 0 || list.size() <= index) {
            return defaultValue;
        }
        return list.get(index);
    }

    public static <T> List<T> ifNull2EmptyList(List<T> list) {
        if (isEmpty(list)) {
            return ListUtil.newArrayList();
        }
        return list;
    }

    public static <T> Collection<T> ifNull2EmptyCollection(Collection<T> list) {
        if (isEmpty(list)) {
            return ListUtil.newArrayList();
        }
        return list;
    }
    /**
     * 转成Map
     *
     * @param collection 既存值列表
     * @param keyMapper  key
     * @return Map对象
     */
    public static <K, T> Map<K, T> toMap(Collection<T> collection, Function<T, K> keyMapper) {
        if (isEmpty(collection)) {
            return new HashMap<>();
        }
        return toMap(collection, keyMapper, Function.identity());
    }

    /**
     * 转成Map
     *
     * @param collection  既存值列表
     * @param keyMapper   key
     * @param valueMapper value
     * @return Map对象
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collection, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        return toMap(collection,null, keyMapper, valueMapper);
    }

    /**
     * 转成Map
     *
     * @param collection      既存值列表
     * @param filterPredicate filter
     * @param keyMapper       key
     * @param valueMapper     value
     * @return Map对象
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collection, Predicate<? super T> filterPredicate, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        return toMap(collection,filterPredicate, keyMapper, valueMapper, (v1, v2) -> v1);
    }

    /**
     * 转成Map
     *
     * @param collection      既存值列表
     * @param filterPredicate filter
     * @param keyMapper       key
     * @param valueMapper     value
     * @return Map对象
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collection, Predicate<? super T> filterPredicate, Function<T, K> keyMapper, Function<T, V> valueMapper, BinaryOperator<V> mergeFn) {
        if (isEmpty(collection)) {
            return new HashMap<>();
        }
        return collection.stream()
                .filter(o-> null == filterPredicate || filterPredicate.test(o))
                //过滤掉value为null的值，因为Collectors.toMap有value为null时会NPE
                .filter(o -> null != valueMapper.apply(o))
                .collect(Collectors.toMap(keyMapper, valueMapper, mergeFn));
    }

    /**
     * 提取出值列表--会过滤掉空对像、及空白字符串
     * @param collection
     * @param valueMapper
     * @return
     * @param <T>
     * @param <V>
     */
    public static <T, V> Set<V> toSet(Collection<T> collection, Function<T, V> valueMapper) {
        if (isEmpty(collection)) {
            return SetUtil.newHashSet();
        }
        return collection.stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toSet());
    }

    /**
     * 提取出值列表--会过滤掉空对像、及空白字符串
     * @param map
     * @param valueMapper
     * @return
     * @param <T>
     * @param <V>
     */
    public static <T,V> Set<V> toSet(Map<V, T> map, Function<T, V> valueMapper) {
        if (null == map || map.isEmpty()) {
            return new HashSet<>();
        }
        return map.values().stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toSet());
    }

    /**
     * 提取出值列表--会过滤掉空对像、及空白字符串
     * @param collection
     * @param valueMapper
     * @return
     * @param <T>
     * @param <V>
     */
    public static <T, V> List<V> toList(Collection<T> collection, Function<T, V> valueMapper) {
        if (isEmpty(collection)) {
            return newArrayList();
        }
        return collection.stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toList());
    }


    /**
     * 据根指定key对列表去重--会过滤掉key为null的数据
     *
     * @param collection
     * @param keyMapper
     * @param mergeFn
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K, V> List<V> distinct(Collection<T> collection, Function<T, K> keyMapper, Function<T, V> valueMapper, BinaryOperator<V> mergeFn) {
        if (isEmpty(collection)) {
            return newArrayList();
        }
        List<K> kList = ListUtil.newArrayList();
        Map<K, Boolean> kmap = new HashMap<>();
        Map<K, V> vmap = new HashMap<>();
        collection.forEach(it -> {
            K k = keyMapper.apply(it);
            V v = valueMapper.apply(it);
            if (null != kmap.get(k)) {
                V v1 = vmap.get(k);
                v = mergeFn.apply(v1, v);
                vmap.put(k, v);
            } else {
                kList.add(k);
                kmap.put(k, true);
                vmap.put(k, v);
            }
        });
        return kList.stream()
                .map(vmap::get)
                .collect(Collectors.toList());
    }

    public static <T, K, V> List<V> distinct(Collection<T> collection, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        return distinct(collection, keyMapper, valueMapper, (v1, v2) -> v1);
    }

    /**
     * 提取数据并去重--会过滤掉值为null的数据
     *
     * @param collection
     * @param valueMapper
     * @param <T>
     * @param <V>
     * @return
     */
    public static <T, V> List<V> toDistinctList(Collection<T> collection, Function<T, V> valueMapper) {
        return distinct(collection, valueMapper, valueMapper);
    }

    /**
     * 据根指定key对列表去重--会过滤掉key为null的数据
     *
     * @param collection
     * @param keyMapper
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K> List<T> distinct(Collection<T> collection, Function<T, K> keyMapper) {
        return distinct(collection, keyMapper, Function.identity());
    }


    //使用toList代替
    @Deprecated
    public static <T> List<String> toStringList(Collection<T> collection, Function<T, String> valueMapper) {
        if (isEmpty(collection)) {
            return ListUtil.newArrayList();
        }
        return collection.stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toList());
    }

    //使用toSet代替
    @Deprecated
    public static <T> Set<String> toStringSet(Collection<T> collection, Function<T, String> valueMapper) {
        if (isEmpty(collection)) {
            return new HashSet<>();
        }
        return collection.stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toSet());
    }

    //使用toSet代替
    @Deprecated
    public static <T> Set<String> toStringSet(Map<String, T> map, Function<T, String> valueMapper) {
        if (null == map || map.isEmpty()) {
            return new HashSet<>();
        }
        return map.values().stream()
                .map(valueMapper)
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toSet());
    }

    public static <T> List<List<T>> splitList(List<T> list, int len) {
        if (isEmpty(list) || len < 1) {
            return null;
        }
        List<List<T>> result = new ArrayList<List<T>>();
        int size = list.size();
        int count = (size + len - 1) / len;
        for (int i = 0; i < count; i++) {
            List<T> subList = list.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));
            result.add(subList);
        }
        return result;
    }

    public static List<String> splitStringToList(String str) {
        return splitStringToList(str, StringPool.COMMA);
    }

    public static List<String> splitStringToList(String str,String separator) {
        if (CheckUtil.isBlank(str) || null == separator) {
            return ListUtil.newArrayList();
        }
        return Arrays.stream(str.split(separator))
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toList());
    }

    public static Set<String> splitStringToSet(String str) {
        if (CheckUtil.isBlank(str)) {
            return SetUtil.newHashSet();
        }
        return Arrays.stream(str.split(StringPool.COMMA))
                .filter(it -> !CheckUtil.isBlank(it))
                .collect(Collectors.toSet());
    }
    /**
     * 笛卡尔集--用于流式处理大量数据
     *
     * @param items
     * @param function 返回true,可主动中断
     * @param <T>
     */
    static public <T> void cartesianProduct(List<List<T>> items, Fn.Fn2<List<T>, List<Integer>, Boolean> function) {
        recurse(items, function, new ArrayList<>(), new ArrayList<>());
    }

    static private <T> Boolean recurse(List<List<T>> items, Fn.Fn2<List<T>, List<Integer>, Boolean> function, List<T> prefix, List<Integer> idxList) {
        if (items.isEmpty()) {
//            consumer.accept(prefix);
            return function.apply(prefix, idxList);
        } else {
            List<T> first = items.get(0);
            List<List<T>> rest = items.subList(1, items.size());
            Boolean stop = false;
            int idx = 0;
            for (T item : first) {
                idx = idx + 1;
                idxList.add(idx);
                prefix.add(item);
                stop = recurse(rest, function, prefix, idxList);
                if (null != stop && stop) {
                    break;
                }
                idxList.remove(idxList.size() - 1);
                prefix.remove(prefix.size() - 1);
            }
            return stop;
        }
    }

    /**
     * 计算笛卡尔集元素数量
     *
     * @param p
     * @param <T>
     * @return
     */
    static public <T> Long calcCartesianProductSize(List<List<T>> p) {
        final long[] size = {1};
        p.forEach(it -> {
            size[0] = size[0] * it.size();
        });
        return size[0];
    }

    /**
     * 计算笛卡尔下标基数
     *
     * @param p
     * @param <T>
     * @return
     */
    static private <T> List<Long> calcCartesianProductIdxBase(List<List<T>> p) {
        List<Long> r = ListUtil.newArrayList();
        long t = 1;
        for (int i = p.size() - 1; i >= 1; i--) {
            t = t * p.get(i).size();
            r.add(t);
        }
        Collections.reverse(r);
        r.add(1L);
        return r;
    }

    /**
     * 反算笛卡尔集输入源数据的下标 -- 用于快速对笛卡尔集进行采样
     *
     * @param src
     * @param pList
     * @return
     */
    static public <T> List<List<Long>> calcCartesianProductSrcIdx(List<List<T>> src, Collection<Long> pList) {
        List<Long> idxBase = calcCartesianProductIdxBase(src);
        List<List<Long>> retList = pList.parallelStream()
                .map(p -> {
                    long v = p - 1;
                    List<Long> idxList = ListUtil.newArrayList();
                    for (int i = 0; i < idxBase.size(); i++) {
                        if (i == idxBase.size()) {
                            idxList.add(v);
                        } else {
                            Long it = idxBase.get(i);
                            long idx = 1;
                            if (v >= it) {
                                idx = v / it + 1;
                            }
                            idxList.add(idx);

                            //将试商的减去
                            Long t = it * (idx - 1);
                            if (v >= t) {
                                v = v - t;
                            }
                        }

                    }
                    return idxList;
                })
                .collect(Collectors.toList());
        return retList;

    }


    public static  <T, P extends ITreeNode<T,P>> List<P>  toTree(List<P> list, String topId) {
        if (isEmpty(list)) {
            return null;
        }
        if (StrUtil.isEmpty(topId)) {
            topId = null;
        }
        List<P> ret = new ArrayList<>(list.size());
        for (P t : list) {
            if (StrUtil.equals(t.getParentId(), topId)) {
                setChild(t, list);
                ret.add(t);
            }
        }
        return ret;
    }

    private static <T,P extends ITreeNode<T,P>> void setChild(P t, List<P> list) {
        for (P n: list) {
            if (StrUtil.isEmpty(n.getParentId())) {
                continue;
            }
            if (n.getParentId().equals(t.getId())) {
                setChild(n, list);
                if (t.getChildren() == null) {
                    t.setChildren(new ArrayList<>());
                }
                t.getChildren().add(n);
            }
        }
    }


    public static void main(String[] args) {
//        List<Integer> i1 = ListUtil.newArrayList(11, 12);
//        List<Integer> i2 = ListUtil.newArrayList(21, 22, 23, 24);
//        List<Integer> i3 = ListUtil.newArrayList(31, 32, 33);
//        List<Integer> i4 = ListUtil.newArrayList(41, 42, 43, 44, 45, 46);
//        List<List<Integer>> s = ListUtil.newArrayList(i1, i2, i3, i4);
//        final int[] count = {0};
//        ListUtil.cartesianProduct(s, (it, idxs) -> {
//            count[0] = count[0] + 1;
//            System.out.print(count[0]);
////            System.out.println(JSON.toJsonStr(it));
//            System.out.println(JSONUtil.toJsonStr(idxs));
//            return null;
//        });
//
//        List<Long> idxBase = calcCartesianProductIdxBase(s);
//        System.out.println(JSONUtil.toJsonStr(idxBase));
//        System.out.println("----------------------------------1");
//        Long c = calcCartesianProductSize(s);
//        for (long i = 1; i <= c; i++) {
//            System.out.print(i);
//            System.out.println(JSONUtil.toJsonStr(calcCartesianProductSrcIdx(s, ListUtil.newArrayList(i))));
//        }
//
//        System.out.println("----------------------------------2");
//        Set<Long> pList = LongStream.range(1, c + 1).boxed()
//                .collect(Collectors.toSet());
//        System.out.println(JSONUtil.toJsonStr(calcCartesianProductSrcIdx(s, pList)));


        List<KeyValue<String, String>> t = ListUtil.newArrayList(
                KeyValue.<String,String>builder()
                        .key("1")
                        .value("1-1")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("1")
                        .value("1-2")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("2")
                        .value("2-1")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("2")
                        .value("2-2")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("2")
                        .value("2-3")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("3")
                        .value("3-1")
                        .build(),
                KeyValue.<String,String>builder()
                        .key("3")
                        .value("3-2")
                        .build()
        );
        List<String> distinct = ListUtil.distinct(t, KeyValue::getKey, KeyValue::getValue,(v1,v2)->v2);
        log.debug(JSONUtil.toJsonStr(distinct));

        BigDecimal v1 = BigDecimal.valueOf(17);
        BigDecimal v2 = BigDecimal.valueOf(6);
        long l = NumberUtil.div(v1, v2).longValue();
        log.debug("{}",l);

        List<Object> objects = Collections.emptyList();
        log.debug("{}",objects.get(0));
    }

}
